/* Copyright 2001,2002,2003 NAH6 BV
 * All Rights Reserved
 *
 *  $Header: /var/lib/cvs/secphone/ui/phone/ThreadMonitor.cpp,v 1.5 2003/11/21 16:12:58 itsme Exp $
 *
 *
 *  this modules should be a standalone app, it monitors the cpu
 *  usage of all ui and machine threads, to be able to easily determine
 *  celp optimisation results.
 *
 */


#include "ThreadMonitor.h"
#include "debug.h"
#include "stringutils.h"

#ifdef _USE_THREADMONITOR

// these include files are in the platformbuilder.
// in PRIVATE\WINCEOS\COREOS\NK\INC and PUBLIC\COMMON\OAK\INC
#define SA1100
#include "PKFUNCS.H"
#include "kernel.h"
#endif

ThreadMonitor::ThreadMonitor()
{
    m_spcore_pid= 0;
    m_ui_pid= 0;
}

ThreadMonitor::~ThreadMonitor()
{
}

DWORD ThreadMonitor::ThreadProc()
{
#ifdef _USE_THREADMONITOR

    BOOL bMode = SetKMode(TRUE);
    DWORD dwPerm = SetProcPermissions(0xFFFFFFFF);

    HANDLE event= CreateEvent(NULL, FALSE, FALSE, NULL);

    while (WaitForSingleObject(event, 5000)==WAIT_TIMEOUT) {
        
        if (m_spcore_pid) {
            ThreadTimeInfoMap spcoretimes;
            GetProcessThreadTimes(m_spcore_pid, spcoretimes);

            debug("--------------SPCORE TIMES\n");
            WriteThreadUsage(m_spcore_times, spcoretimes);
            m_spcore_times= spcoretimes;
        }
        else {
            m_spcore_pid= GetProcessId("spcore.exe");
        }

        if (m_ui_pid) {
            ThreadTimeInfoMap uitimes;
            GetProcessThreadTimes(m_ui_pid, uitimes);

            debug("--------------UI TIMES\n");
            WriteThreadUsage(m_ui_times, uitimes);
            m_ui_times= uitimes;
        }
        else {
            m_ui_pid= GetProcessId("CryptoPhone.exe");
        }
    }

    SetProcPermissions(dwPerm);
    SetKMode(bMode);
#endif
    return 0;
}
void ThreadMonitor::GetProcessThreadTimes(DWORD pid, ThreadTimeInfoMap& times)
{
#ifdef _USE_THREADMONITOR
    HDATA *hdata= (HDATA *)(0x80000000+((DWORD)pid&0x1ffffffc));

    PROCESS *pproc= (PROCESS *)hdata->pvObj;

    times.clear();

    THREAD *pth= pproc->pTh;
    while(pth)
    {
        ThreadTimeInfo &info= times[(DWORD)pth->hTh];

        info.kernel  = pth->dwKernTime;
        info.user    = pth->dwUserTime;
        info.current = GetTickCount();

        pth= pth->pNextInProc;
    }
#endif
}
DWORD ThreadMonitor::GetProcessId(const std::string& processname)
{
    DWORD dwProc= 0;
#ifdef _USE_THREADMONITOR
    HANDLE hTH= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    std::wstring wprocname= ToWString(processname);

    PROCESSENTRY32 pe;
    pe.dwSize= sizeof(PROCESSENTRY32);

    if (Process32First(hTH, &pe))
    {
        do {

            if (wcsicmp(wprocname.c_str(), pe.szExeFile)==0)
            {
                dwProc= pe.th32ProcessID;
                break;
            }
        } while (Process32Next(hTH, &pe));
    }

    CloseToolhelp32Snapshot(hTH);
#endif
    return dwProc;
}
void ThreadMonitor::WriteThreadUsage(const ThreadTimeInfoMap& prev, const ThreadTimeInfoMap& cur)
{
    double pmUserTotal= 0;
    double pmKernelTotal= 0;
    for (ThreadTimeInfoMap::const_iterator iPrev= prev.begin() ; iPrev!=prev.end() ; ++iPrev)
    {
        ThreadTimeInfoMap::const_iterator iCur= cur.find((*iPrev).first);
        if (iCur==cur.end())
        {
            debug("%08lx  - thread terminated\n", (*iCur).first);
        }
        else {
            DWORD measurementtime = (*iCur).second.current - (*iPrev).second.current;

            if (measurementtime==0) {
                debug("%08lx - ------- -------  -------\n", (*iPrev).first);
            }
            else {
                double pmUser= (((*iCur).second.user - (*iPrev).second.user)*100.0) / measurementtime;
                double pmKernel= (((*iCur).second.kernel - (*iPrev).second.kernel)*100.0) / measurementtime;

                pmUserTotal += pmUser;
                pmKernelTotal += pmKernel;

                debug("%08lx - %7.2f %7.2f  %7.2f\n", (*iPrev).first, pmUser, pmKernel, pmUser+pmKernel);
            }
        }
    }
    for (ThreadTimeInfoMap::const_iterator iCur= cur.begin() ; iCur!=cur.end() ; ++iCur)
    {
        if (prev.find((*iCur).first)==prev.end())
        {
            debug("%08lx  - thread created\n", (*iCur).first);
        }
    }
    debug("total   - %7.2f %7.2f  %7.2f\n", pmUserTotal, pmKernelTotal, pmUserTotal+pmKernelTotal);
}
